package thinkinjava4.atunit;

import thinkinjava4.util.BinaryFile;
import thinkinjava4.util.Directory;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import static thinkinjava4.util.Print.print;

/**
 * 在class文件中获取类名
 */
public class ClassNameFinder {
    /**
     * 读取class文件
     *
     * @param classBytes class文件
     * @return 类名
     */
    public static String thisClass(byte[] classBytes) {
        Map<Integer, Integer> offsetTable = new HashMap<>();
        Map<Integer, String> classNameTable = new HashMap<>();
        try {
            DataInputStream data = new DataInputStream(new ByteArrayInputStream(classBytes));
            int magic = data.readInt();
            int minorVersion = data.readShort();
            int majorVersion = data.readShort();
            int constantPoolCount = data.readShort();
            int[] constantPool = new int[constantPoolCount];
            for (int i = 1; i < constantPoolCount; i++) {
                int tag = data.read();
                int tableSize;
                switch (tag) {
                    case 1://UTF
                        int length = data.readShort();
                        char[] bytes = new char[length];
                        for (int k = 0; k < bytes.length; k++)
                            bytes[k] = (char) data.read();
                        String className = new String(bytes);
                        classNameTable.put(i, className);
                        break;
                    case 5://LONG
                    case 6://DOUBLE
                        data.readLong();
                        i++;
                        break;
                    case 7://CLASS
                        int offset = data.readShort();
                        offsetTable.put(i, offset);
                        break;
                    case 8://STRING
                        data.readShort();
                        break;
                    case 3://INTEGER
                    case 4://FLOAT
                    case 9://FIELD_REF
                    case 10://METHOD_REF
                    case 11://INTERFACE_METHOD_REF
                    case 12://NAME_AND_TYPE
                        data.readInt();
                        break;
                    default:
                        throw new RuntimeException("Bad tag " + tag);
                }
            }
            short accessFlags = data.readShort();
            int thisClass = data.readShort();
            int superClass = data.readShort();
            return classNameTable.get(offsetTable.get(thisClass)).replace('/', '.');
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
//out/production/workspace/thinkinjava4/ch20_annotations/AtUnitExample1.class
    public static void main(String[] args) throws Exception {
        if (args.length > 0) {
            for (String arg : args)
                print(thisClass(BinaryFile.read(new File(arg))));
        } else {
            for (File klass : Directory.walk("thinkinjava4.ch20_annotations", ".*\\.class"))
                print(thisClass(BinaryFile.read(klass)));
        }
    }
}
